require("classes")

require("board")

Class	{	'Pentago'
		,	player1_name	= String
		,	player2_name	= String

		,	is_interactive	= Boolean	-- interactive/console

		,	ki_enemy		= Boolean
		,	current_player	= Number	-- 1 or 2
		,	movement_state	= Number	-- can be 1 (waiting for current player to place his tile) or 2 (current player has to rotate)

		,	board			= Board		-- our board
		,	winner 			= Number 	-- can be 0('none' - no winner yet),1 or 2, 3('draw')
	 }

function Pentago:create(p1, p2)
	if p1 == nil then
		error("Error: there has to be at least one Player")
	end
	self.player1_name	= p1

	if p2 == nil then
		self.ki_enemy = true
		self.player2_name	= "KI"
	else
		self.ki_enemy = false
		self.player2_name	= p2
	end

	self.current_player	= 1
	self.movement_state	= 1

	self.winner 		= 0

	self.board			= Board:create()
end

function Pentago:start (mode)
	self.is_interactive = mode

	if self.is_interactive then
		while self.winner == 0 do	-- as long there is no winner, the game is still running
			if self.is_interactive then
				if		self.current_player == 1
					or	not self.ki_enemy
				then								-- a human player's turn
					local move = io.read()
					while not self:makeMove(move) do
						move = io.read()
					end
				end
			end
		end
	end
end

function Pentago:makeMove(move)	-- String with the correct letter-number/letter-letter combination, representing
									-- stone placement/square rotaion on the board
	-- parsing of the input string depending on the current movement state

	-- check for resign:
	if move == 'resign' then
		self:resign()
		return true
	end

	-- print the move:
	print ("Input:  " .. move)

	local quadrant
	local command
	local position_mapping =	{	a = {{1,1},{1,2},{1,3},{2,1},{2,2},{2,3},{3,1},{3,2},{3,3}}
								,	b = {{1,4},{1,5},{1,6},{2,4},{2,5},{2,6},{3,4},{3,5},{3,6}}
								,	c = {{4,1},{4,2},{4,3},{5,1},{5,2},{5,3},{6,1},{6,2},{6,3}}
								,	d = {{4,4},{4,5},{4,6},{5,4},{5,5},{5,6},{6,4},{6,5},{6,6}}
								}

	if self.movement_state == 1 then	-- player has to place his tile
		if move:lower() == move:lower():match("%s*[a-d]%s*[1-9]%s*") then			-- we allow whitespaces everywhere ;-)
			quadrant, command = move:lower():match("%s*([a-d])%s*([1-9])%s*")
		else
			print ("This move is not allowed.")
			return false
		end
	else								-- player has to rotate a square
		if move:lower() == move:lower():match("%s*[a-d]%s*[rl]%s*") then
			quadrant, command = move:lower():match("%s*([a-d])%s*([rl])%s*")
		else
			print ("This move is not allowed.")
			return false
		end
	end


	if self.movement_state == 1 then -- place tile on board
		local position = position_mapping[quadrant][command+0]
		local x = position[1]
		local y = position[2]

		if not self.board:set_stone(x,y,self.current_player) then
			print ("There is already a tile at that position. Place again.")
			return false
		end
	else -- rotate square
		if command == 'r' then
			self.board:_turn_CW(quadrant)
		else
			self.board:_turn_CCW(quadrant)
		end
	end

	if self.board:_check_5(1) then
		self.winner = 1
	end
	if self.board:_check_5(2) then
--		if self.winner == 1 then
--			self.winner = 3
--		else
			self.winner = 2
--		end
	end

	if		self.winner == 0
		and	self.board.movesLeft == 0
	then
		self.winner = 3
	end

	if self.movement_state == 1 then
		self.movement_state = 2
	else
		self.movement_state = 1
		self.current_player = (self.current_player % 2) + 1
	end

	return true
end

function Pentago:resign()
	if self.current_player == 1 then
		self.winner = 2
	else
		self.winner = 1
	end
end

function Pentago:verbose()
	Verbose:activate()
end

function Pentago:silent()
	Verbose:deactivate()
end

function Pentago:printGame()
	self.board:print()
end

function Pentago:getWinner()
	if self.winner == 0 then
		return 'none'
	elseif self.winner == 3 then
		return 'draw'
	elseif self.winner == 1 then
		return self.player1_name
	elseif self.winner == 2 then
		return self.player2_name
	end
end



Aspect	{	'Verbose'
		,	adapts	= { Pentago }
		,	before	= { beforeStart = 'start' }
		,	after	= { afterMove = 'makeMove' }
}

function Verbose:beforeStart(methodName, mode)
	self:printGame()
	print()
	print("Game started.")

	local playerName
	local playerColor
	local command

	if self.movement_state == 1 then
		command		= "place"
	else
		command		= "rotate"
	end

	if self.current_player == 1 then
		playerName	= self.player1_name
		playerColor	= "white"
	else
		playerName = self.player2_name
		playerColor	= "black"
	end

	print("* " .. playerName .. " [" .. playerColor .. "], please " .. command .. ":\n")
	return true
end

function Verbose:afterMove(methodName, was_valid_move, move)

	if self.winner ~= 0 then
		return true
	end

	if was_valid_move then
		self:printGame()
		print()

		local playerName
		local playerColor
		local command

		if self.movement_state == 1 then
			command		= "place"
		else
			command		= "rotate"
		end

		if self.current_player == 1 then
			playerName	= self.player1_name
			playerColor	= "white"
		else
			playerName	= self.player2_name
			playerColor	= "black"
		end

		print("* " .. playerName .. " [" .. playerColor .. "], please " .. command .. ":\n")
		return true
	end
	return false
end

Aspect	{	'KI'
		,	adapts	= { Pentago}
		,	after	= { move_ki = 'makeMove'}
}

function KI:move_ki(methodName, result, move)
	if		self.ki_enemy
		and	self.current_player == 2
	then
		if self.movement_state == 1 then
			local freeFields	= self.board:getFreeFields()
			local chosen_field	= freeFields[math.random(#freeFields)]
			self:makeMove(chosen_field)
		else
			local randomSquareNumber = math.random(4)
			local square
			if randomSquareNumber == 1 then
				square = 'a'
			elseif randomSquareNumber == 2 then
				square = 'b'
			elseif randomSquareNumber == 3 then
				square = 'c'
			else
				square = 'd'
			end

			local randomRotateNumber = math.random(2)
			local rotate
			if randomRotateNumber == 1 then
				rotate = 'r'
			else
				rotate = 'l'
			end

			self:makeMove(square .. rotate)
		end
	end

	return result
end

math.randomseed( os.time() )

Verbose:activate()
KI:activate()
--[[
game = Pentago:create("Eik", "Bogdan")
game:start(true)

print("Gewinner:")
print( game:getWinner() )
--game:makeMove("C6")
--game:printGame()

]]
